﻿using HARTCalibrationTool.Commands;
using HARTCalibrationTool.Commands.BatchCalibrationBoard;
using HARTCalibrationTool.Commands.HART;
using HARTCalibrationTool.Commands.HART.CustomCommands;
using HARTCalibrationTool.Commands.PressureController;
using HARTCalibrationTool.SerialPortManager;
using System;
using System.IO.Ports;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HARTCalibrationTool.AutomaticCalibration
{
    public enum AutoCalibrationCMDsType
    {
        eAutoCMD_None = 0x00,

        // Temperatue Box
        eAutoCMD_TemperatureBoxStart = 0x01,
        eAutoCMD_TemperatureBoxSetTemperature = 0x02,
        eAutoCMD_TemperatureBoxStop = 0x03,

        // Pressure Controller
        eAutoCMD_PressureControl_Pressurization = 0x10,
        eAutoCMD_PressureControl_PressureRelief = 0x11,
        eAutoCMD_PressureControl_SetUnit = 0x12,
        eAutoCMD_PressureControl_SetPressure = 0x13,
        eAutoCMD_PressureControl_QueryUnit = 0x14,
        eAutoCMD_PressureControl_QueryPressure = 0x15,

        // HART
        eAutoCMD_HART_GetFitData = 0x20,
        eAutoCMD_HART_SwitchChannel = 0x21,

        // Batch Board
        eAutoCMD_BatchBoard_LED_R_Flashing = 0x30,
        eAutoCMD_BatchBoard_LED_G_Flashing = 0x31,
        eAutoCMD_BatchBoard_LED_B_Flashing = 0x32,
        eAutoCMD_BatchBoard_LED_R_On = 0x33,
        eAutoCMD_BatchBoard_LED_G_On = 0x34,
        eAutoCMD_BatchBoard_Pump_Open = 0x35,
        eAutoCMD_BatchBoard_LED_R_Off = 0x36,
        eAutoCMD_BatchBoard_LED_G_Off = 0x37,
        eAutoCMD_BatchBoard_Pump_Close = 0x38,

        // Retest
        eRetest_HART1ReadPV = 0x40,
        eRetest_HART2ReadLoopCurrent = 0x41,
    }

    public class AutomaticCalibrationManager
    {
        private TaskCompletionSource<bool> _tcs = new TaskCompletionSource<bool>();
        private SerialPortsBase _serialPort = null;
        private AutoCalibrationCMDsType _autoCmdType = AutoCalibrationCMDsType.eAutoCMD_None;
        private object[] _args = null;

        public AutomaticCalibrationManager(SerialPortsBase serialPort, AutoCalibrationCMDsType cmdType, object[] args = null) 
        {
            _serialPort = serialPort;
            _autoCmdType = cmdType;
            _args = args;
        }

        public async Task<bool> AutomaticCaliCMDsStart()
        {
            int retryCnt = 0;
            bool result = false;
            bool isSuccess = false;
            int MaxRetries = 5;
            CommandsBase cmd = null;

            if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_TemperatureBoxStart)
            {
                cmd = new AutoCaliTemperatureBoxCMDs(_serialPort, "Start Running", TemperatureBoxCMDsType.eStartRunning, null);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_TemperatureBoxStop)
            {
                cmd = new AutoCaliTemperatureBoxCMDs(_serialPort, "Stop Running", TemperatureBoxCMDsType.eStopRunning, null);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_TemperatureBoxSetTemperature)
            {
                cmd = new AutoCaliTemperatureBoxCMDs(_serialPort, "Set Temperature", TemperatureBoxCMDsType.eSetTemp, _args);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_Pressurization)
            {
                cmd = new AutoCaliPressureControllerCMDs(_serialPort, "Pressurization", PressureControllerCMDsType.ePressurization);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_PressureRelief)
            {
                cmd = new AutoCaliPressureControllerCMDs(_serialPort, "PressureRelief", PressureControllerCMDsType.ePressureRelief);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_SetUnit)
            {
                cmd = new AutoCaliPressureControllerCMDs(_serialPort, "Set Unit", PressureControllerCMDsType.eSetUnit, _args);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_SetPressure)
            {
                cmd = new AutoCaliPressureControllerCMDs(_serialPort, "Set Pressure", PressureControllerCMDsType.eSetPressure, _args);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_QueryUnit)
            {
                cmd = new AutoCaliPressureControllerCMDs(_serialPort, "Query Unit", PressureControllerCMDsType.eQueryUnit);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_QueryPressure)
            {
                cmd = new AutoCaliPressureControllerCMDs(_serialPort, "Query Act Pressure", PressureControllerCMDsType.eQueryCurActPressure, _args);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_HART_SwitchChannel)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchHARTChlCMD(_serialPort, "Switch Channel", (CMDsChlID)(_args[0]));
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_HART_GetFitData)
            {
                int currentNum = (int)_args[0];
                string modeType = (string)_args[1]; // 行程
                ListView listViewSrcData = (ListView)_args[2];

                cmd = new AutoCaliHART135CustomCommandFitGetInfo(_serialPort, "HART#135#08", CustomCommandsBase.GET_INFO, (byte)currentNum, modeType, listViewSrcData);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_BatchBoard_LED_R_Flashing)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchRGB(_serialPort, "RGB R Flashing", RGBSel.eRGB_R_Flashing);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_BatchBoard_LED_G_Flashing)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchRGB(_serialPort, "RGB G Flashing", RGBSel.eRGB_G_Flashing);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_BatchBoard_LED_R_On)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchRGB(_serialPort, "RGB R On", RGBSel.eRGB_R_ON);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_BatchBoard_LED_G_On)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchRGB(_serialPort, "RGB G On", RGBSel.eRGB_G_ON);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_BatchBoard_LED_R_Off)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchRGB(_serialPort, "RGB R Off", RGBSel.eRGB_R_OFF);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_BatchBoard_LED_G_Off)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchRGB(_serialPort, "RGB G Off", RGBSel.eRGB_G_OFF);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_BatchBoard_Pump_Open)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchRGB(_serialPort, "Pump Open", RGBSel.eVacuum_Pump_ON);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_BatchBoard_Pump_Close)
            {
                cmd = new AutoCaliBatchCaliBoardCMDsSwitchRGB(_serialPort, "Pump Close", RGBSel.eVacuum_Pump_OFF);
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eRetest_HART1ReadPV)
            {
                cmd = new RetestHART1ReadPV(_serialPort, "Retest Read PV");
            }
            else if (_autoCmdType == AutoCalibrationCMDsType.eRetest_HART2ReadLoopCurrent)
            {
                cmd = new RetestHART2ReadLoopCurrent(_serialPort, "Retest Read Loop Current");
            }

            if (cmd == null)
            {
                return false;
            }

            cmd.ClrCurrentCMDResponseFinishedFlg();

            _serialPort.SetCurrentCMD(cmd);

            while (true)
            {
                try
                {
                    var commandTask = cmd.ExecuteCMD();
                    var timeoutTask = Task.Delay(TimeSpan.FromSeconds(2));

                    var completedTask = await Task.WhenAny(commandTask, timeoutTask);

                    if (commandTask == timeoutTask)
                    {
                        retryCnt++;

                        if (retryCnt > MaxRetries)
                        {
                            result = false;
                            break;
                        }
                        else
                        {
                            await Task.Delay(TimeSpan.FromSeconds(2));
                            continue;
                        }
                    }
                    else
                    {
                        // 没有返回信息的命令，直接认为发送成功返回true
                        if ((_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_SetUnit) ||
                            (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_SetPressure) || 
                            (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_Pressurization) ||
                            (_autoCmdType == AutoCalibrationCMDsType.eAutoCMD_PressureControl_PressureRelief))
                        {
                            result = true;
                            break;
                        }
                        else
                        {
                            isSuccess = await AutoCaliWaitForResponseAsync(_serialPort, cmd);

                            if (isSuccess)
                            {
                                result = true;
                                break;
                            }
                            else
                            {
                                retryCnt++;

                                if (retryCnt > MaxRetries)
                                {
                                    result = false;
                                    break;
                                }
                                else
                                {
                                    continue;
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    SerialPortsBase.PrintMessage(ex.Message);
                    break;
                }
            }

            return result;
        }

        private async Task<bool> AutoCaliWaitForResponseAsync(SerialPortsBase serialPort, CommandsBase command)
        {
            var tcs = new TaskCompletionSource<bool>();
            EventHandler handler = (s, e) => tcs.TrySetResult(true);
            bool isSuccess = false;

            try
            {
                serialPort.CommandCompleted += handler;

                var timeoutTask = Task.Delay(TimeSpan.FromSeconds(5));
                var responseTask = tcs.Task;

                var completedTask = await Task.WhenAny(responseTask, timeoutTask);

                if (completedTask == timeoutTask)
                {
                    command.PrintMessage("Command:" + command.CommandName + ", 响应超时！");
                }
                else
                {
                    command.CommandSuccess += OnCommandSuccess;

                    if (command.IsCurrentCMDResponseFinished())
                    {
                        isSuccess = true;
                    }
                    else
                    {
                        var timeoutResponseTask = Task.Delay(TimeSpan.FromSeconds(5));
                        var tcsResponseTask = _tcs.Task;

                        var cmdCompletedTask = await Task.WhenAny(tcsResponseTask, timeoutResponseTask);

                        if (cmdCompletedTask == timeoutResponseTask)
                        {
                            isSuccess = false;
                        }
                        else
                        {
                            isSuccess = true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                command.PrintMessage(ex.ToString());
            }
            finally
            {
                serialPort.CommandCompleted -= handler;
            }

            return isSuccess;
        }

        private void OnCommandSuccess(object sender, EventArgs e)
        {
            //_tcs.SetResult(true);

            if (!_tcs.Task.IsCompleted)
            {
                _tcs.SetResult(true);
            }
        }

        public void AutoCaliSetCurrentCMD(AutoCalibrationCMDsType cmdType)
        {
            _autoCmdType = cmdType;
        }

        public void AutoCaliSetCurrentSerialPort(SerialPortsBase serialPort)
        {
            _serialPort = serialPort;
        }

        public void AutoCaliSetArgs(object[] argsObj)
        { 
            _args = argsObj;
        }
    }
}
